玩转 Spring Boot 集成篇(定时任务框架Quartz) | 您所在的位置:网站首页 › 定时任务 java › 玩转 Spring Boot 集成篇(定时任务框架Quartz) |
在日常项目研发中,定时任务可谓是必不可少的一环,关于 Spring Boot 如何实现静态定时任务、动态定时任务以及如何开启多线程跑任务,均已在上篇分享过,不再赘述。 虽然 Spring Boot 内置注解方式实现的定时任务,在一定程度上也能解决一定的业务场景问题,但是若做更复杂的动作,例如启停任务、删除任务等等操作,实现起来则稍显复杂,此时便可以通过集成开源任务框架来实现。 常见的定时任务框架有 Quartz、elastic-job、xxl-job等等,本次主要介绍 Spring Boot 集成定时任务第 3 部分:Spirng Boot 集成 Quartz 定时任务框架。 ![]() Quartz 存储方式有两种:MEMORY 和 JDBC。默认是内存形式维护任务信息,意味着服务重启了任务就从头再来,就像喝酒断片了一样;而 JDBC 形式就是能够把任务信息持久化到数据库,虽然服务重启了,依然还能接着来。 Quartz 提供了单机版和集群版,默认就是单机版,接下来逐一分享一波。 Spring Boot 集成 Quartz 的方式也很简单,首先引入封装好的 Quartz 依赖。 org.springframework.boot spring-boot-starter-quartz1. 内存方式存储任务信息 1.1 定义任务类 可以通过实现 Job 接口来定义任务,也可以通过继承 QuartzJobBean 这个抽象类来定义任务,其实 QuartzJobBean 本身也实现了 Job 接口,其本质都是实现 Job 接口来定义任务。 import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; /** * 定义任务 */ public class DongAoJob extends QuartzJobBean { private static final Log logger = LogFactory.getLog(DongAoJob.class); @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { logger.info("幼年是盼盼,青年是晶晶,中年是冰墩墩,生活见好逐渐发福"); } }1.2 定义任务描述及任务触发规则 定义每隔 5 秒执行一次任务,代码如下。 import com.example.demo.quartz.task.DongAoJob; import org.quartz.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 定义任务描述和具体的执行时间 */ @Configuration public class QuartzConfig { @Bean public JobDetail jobDetail() { //指定任务描述具体的实现类 return JobBuilder.newJob(DongAoJob.class) // 指定任务的名称 .withIdentity("dongAoJob") // 任务描述 .withDescription("任务描述:用于输出冬奥欢迎语") // 每次任务执行后进行存储 .storeDurably() .build(); } @Bean public Trigger trigger() { //创建触发器 return TriggerBuilder.newTrigger() // 绑定工作任务 .forJob(jobDetail()) // 每隔 5 秒执行一次 job .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(5)) .build(); } }1.3 程序入口 main 函数 import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoJobApplication { public static void main(String[] args) { SpringApplication.run(DemoJobApplication.class, args); } }1.4 运行验证 ![]() 通过控制台输出能够看到每 5 秒输出一次信息,达到预期效果。 通过控制台标注 1 的部分能够看到默认采用的是 RAMJobStore,就是将任务相关信息保存在内存里,应用重启后,定时任务信息将会丢失。 2. 数据库方式存储任务信息 2.1 引入依赖 在 pom.xml 文件中加入如下依赖信息。 com.mchange c3p0 0.9.5.4 mysql mysql-connector-java2.2 添加 Quartz 配置信息 在 application.properties 文件中加入 Quartz 相关配置。 # 将 Quartz 持久化方式修改为 jdbc spring.quartz.job-store-type=jdbc # 实例名称(默认为quartzScheduler) spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler # 实例节点 ID 自动生成 spring.quartz.properties.org.quartz.scheduler.instanceId=AUTO # 修改存储内容使用的类 spring.quartz.properties.org.quartz.jobStore.class=org.quartz.impl.jdbcjobstore.JobStoreTX # 数据源信息 spring.quartz.properties.org.quartz.jobStore.dataSource=quartz_jobs spring.quartz.properties.org.quartz.dataSource.quartz_jobs.driver=com.mysql.cj.jdbc.Driver spring.quartz.properties.org.quartz.dataSource.quartz_jobs.URL=jdbc:mysql://127.0.0.1:3306/quartz_jobs?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 spring.quartz.properties.org.quartz.dataSource.quartz_jobs.user=root spring.quartz.properties.org.quartz.dataSource.quartz_jobs.password=1234562.3 初始化 Quartz 数据表信息 下载 Quartz 发布包,下载完成后,解压缩进入 quartz-2.2.3/docs/dbTables 目录,找到匹配数据库的 SQL 文件。 下载地址: https://www.quartz-scheduler.org/downloads/files/quartz-2.2.3-distribution.tar.gz为了方便演示,单独创建 quartz_jobs 数据库。 create database quartz_jobs default charset utf8; use quartz_jobs;然后在数据库中执行 tables-mysql.sql 初始化脚本,脚本内容如下(来源于官网压缩包),先别细究每个表的意义,用起来才是硬道理。 DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; DROP TABLE IF EXISTS QRTZ_LOCKS; DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; DROP TABLE IF EXISTS QRTZ_TRIGGERS; DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; DROP TABLE IF EXISTS QRTZ_CALENDARS; CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT(13) NULL, PREV_FIRE_TIME BIGINT(13) NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT(13) NOT NULL, END_TIME BIGINT(13) NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT(2) NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ); CREATE TABLE QRTZ_SIMPLE_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, REPEAT_COUNT BIGINT(7) NOT NULL, REPEAT_INTERVAL BIGINT(12) NOT NULL, TIMES_TRIGGERED BIGINT(10) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CRON_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, CRON_EXPRESSION VARCHAR(200) NOT NULL, TIME_ZONE_ID VARCHAR(80), PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_SIMPROP_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, STR_PROP_1 VARCHAR(512) NULL, STR_PROP_2 VARCHAR(512) NULL, STR_PROP_3 VARCHAR(512) NULL, INT_PROP_1 INT NULL, INT_PROP_2 INT NULL, LONG_PROP_1 BIGINT NULL, LONG_PROP_2 BIGINT NULL, DEC_PROP_1 NUMERIC(13,4) NULL, DEC_PROP_2 NUMERIC(13,4) NULL, BOOL_PROP_1 VARCHAR(1) NULL, BOOL_PROP_2 VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_BLOB_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, BLOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) REFERENCES QRTZ_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_CALENDARS ( SCHED_NAME VARCHAR(120) NOT NULL, CALENDAR_NAME VARCHAR(200) NOT NULL, CALENDAR BLOB NOT NULL, PRIMARY KEY (SCHED_NAME,CALENDAR_NAME) ); CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_GROUP) ); CREATE TABLE QRTZ_FIRED_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, ENTRY_ID VARCHAR(95) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, FIRED_TIME BIGINT(13) NOT NULL, SCHED_TIME BIGINT(13) NOT NULL, PRIORITY INTEGER NOT NULL, STATE VARCHAR(16) NOT NULL, JOB_NAME VARCHAR(200) NULL, JOB_GROUP VARCHAR(200) NULL, IS_NONCONCURRENT VARCHAR(1) NULL, REQUESTS_RECOVERY VARCHAR(1) NULL, PRIMARY KEY (SCHED_NAME,ENTRY_ID) ); CREATE TABLE QRTZ_SCHEDULER_STATE ( SCHED_NAME VARCHAR(120) NOT NULL, INSTANCE_NAME VARCHAR(200) NOT NULL, LAST_CHECKIN_TIME BIGINT(13) NOT NULL, CHECKIN_INTERVAL BIGINT(13) NOT NULL, PRIMARY KEY (SCHED_NAME,INSTANCE_NAME) ); CREATE TABLE QRTZ_LOCKS ( SCHED_NAME VARCHAR(120) NOT NULL, LOCK_NAME VARCHAR(40) NOT NULL, PRIMARY KEY (SCHED_NAME,LOCK_NAME) ); commit;2.4 运行验证 代码无需任何变动,直接运行 main 函数,控制台输出如下: ![]() 通过控制台输出发现任务正常跑起来,而且任务存储的方式变为了 jdbcjobstore.JobStoreTx,此时数据库表中也有任务信息插入了。 ![]() 至此,Spring Boot 集成 Quartz 的两种存储任务方式就完事儿了,其实很简单,稍显复杂的多是 Quartz 相关的持久化依赖的 SQL 及配置信息。 但是,在某些业务场景下要求任务必须高可用、可扩展,那么单台服务器不能满足业务需求,这时就需要开启 Quartz 分布式定时任务啦。 3. 分布式任务支持 3.1 开启集群配置 在 application.properties 文件中,加入 Quartz 集群的配置信息。 # 开启集群,多个 Quartz 实例使用同一组数据库表 spring.quartz.properties.org.quartz.jobStore.isClustered=true注意 Quartz 使用同一组数据库表作集群时,只需要配置相同的 instanceName 实例名称就可以,例如本次都用 SC_Scheduler。 spring.quartz.properties.org.quartz.scheduler.instanceName=SC_Scheduler3.2 运行验证 为了方便启动多实例验证,把服务启动的端口进行随机生成,application.properties 文件加入配置如下。 server.port=${random.int[10000,19999]}直接运行 main 函数,启动实例 1;再次运行 main 函数,启动实例2(如果 IDEA 不让重复运行,那就重新复制一个启动类,再运行)。 ![]() 通过红色圈住部分 is clustered,显然已是开启了集群。若关闭其中一个正在跑任务的节点,观察另一个节点是否会自动检测继续执行任务呢? 停掉节点 1: ![]() 节点 2 继续接着执行任务: ![]() 至此,Spring Boot 集成 Quartz 可以接近尾声了。 不过稍显不甘心呢,因为回头看代码,会发现 QuartzConfig 中的 job 和 trigger 都是硬编码方式完成的。 ![]() 这样肯定无法管理任务的状态,无法做到扩展,更达不到 Spring Boot 的思想让程序员更专注业务开发,所以仍有改进的空间,仍存很多疑问。 疑问:是否可以通过 API 动态创建任务呢? 疑问:是否可以通过 API 编辑任务的执行时间呢? 疑问:是否可以通过 API 暂停/恢复任务呢? 疑问:是否可以通过 API 删除任务呢? 疑问:是否可以 ... ... 此时,可以这么回答:of course.Im very sure. 老鼠拉木锨——大头在后面呢,不过限于自定义代码较多,单独放一代码篇去分享。 4. 例行回顾 本文是 Spring Boot 项目集成 Quartz 定时任务框架讲解,主要分享了如下部分: 默认内存方式存储任务信息;数据库方式任务信息;分布式任务支持玩转 Spring Boot 集成 Quartz 定时任务就写到这里,下次一起编码实现 Quartz 任务的动态管理。 历史系列文章: 玩转 Spring Boot 入门篇 玩转 Spring Boot 集成篇(MySQL、Druid、HikariCP) 玩转 Spring Boot 集成篇(MyBatis、JPA、事务支持) 玩转 Spring Boot 集成篇(Redis) 玩转 Spring Boot 集成篇(Actuator、Spring Boot Admin) 玩转 Spring Boot 集成篇(RabbitMQ) 玩转 Spring Boot 集成篇(@Scheduled、静态、动态定时任务) |
今日新闻 |
推荐新闻 |
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 |